﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Drawing;

/* ############################################################################################
 *           Matthijs Hilgers - Gerben Boot - Marcel Boelen - Joep van den Hoven
 *               1580499          1575754        1582702            1592146         
 * ############################################################################################
 * 
 *  Date     || Changed                                  || By              || Source
 *  ===========================================================================================
 *  28-03-12 || First version                            || Gerben          || new
 */  

namespace MultipleOutputNeuralNetwork {
    class NeuralNetwork {
        private Node[] inputNodes;
        private Node[] hiddenNodes;
        private Node[] outputNodes;
        private int numberOfInputs;

        public NeuralNetwork(int numberOfInputs, int numberOfOutputs) {
            this.numberOfInputs = numberOfInputs;
            inputNodes = new Node[numberOfInputs];
            for (int i = 0; i < inputNodes.Length; i++) {
                inputNodes[i] = new Node(1);
            }

            hiddenNodes = new Node[numberOfOutputs];
            for (int i = 0; i < hiddenNodes.Length; i++) {
                hiddenNodes[i] = new Node();
                //voeg elke input node toe aan hiddennode
                foreach (Node inputNode in inputNodes) {
                    hiddenNodes[i].addInputConnection(new NodeConnection(inputNode));
                }
            }

            outputNodes = new Node[numberOfOutputs];
            for (int i = 0; i < outputNodes.Length; i++) {
                //voeg hidden node toe aan outputNode
                outputNodes[i] = new Node(1);
                outputNodes[i].addInputConnection(new NodeConnection(hiddenNodes[i],1));
            }
        }

        public void runNodes(Node[] nodes) {
            HashSet<Thread> threads = new HashSet<Thread>();
            foreach (Node node in nodes) {
                Thread t = new Thread(new ThreadStart(node.run));
                t.Start();
                threads.Add(t);
            }

            foreach (Thread t in threads) {
                t.Join();
            }
        }

        public char getChar(System.Drawing.Bitmap InputImage) {
            Bitmap bmp = new CropAlgorithm().cropImage(InputImage);
            setInputNodes(new Bitmap(bmp));
            runNodes(hiddenNodes);
            runNodes(outputNodes);
            byte outputValue = 0;
            int shift = 6;
            foreach (Node node in outputNodes) {
                outputValue = (byte)(outputValue | (node.value << shift));
                shift--;
            }
            char c = Convert.ToChar(outputValue);
            return c;
        }

        public Boolean learn(Bitmap InputImage, char c) {
            Bitmap bmp = new CropAlgorithm().cropImage(InputImage);
            setInputNodes(new Bitmap(bmp));
            int charValue = Convert.ToInt16(c);
            byte mask = 0x40;
            Boolean b = false;
            foreach (Node node in hiddenNodes) {
                int output = (charValue & mask) > 0 ? 1 : 0;
                if (node.learn(output))
                    b = true;
                mask = (byte)(mask >> 1);
            }
            return b;
        }

        private void setInputNodes(Bitmap bmp) {
            ImageData imageData = new ImageData();
            byte[] rgbValues = imageData.bitmapToArray(bmp);
            double d = Math.Sqrt(numberOfInputs);
            double widthStep = imageData.imageWidth / d;
            double heighStep = imageData.imageHeight / d;

            int count = 0;
            double heightValue = 0;
            for (double height = 0; (int)height + heighStep +1 <= imageData.imageHeight; height += heighStep) {
                for (double width = 0; (int)width + widthStep +1 <= imageData.imageWidth; width += widthStep) {
                    int value = rgbValues[(int)(heightValue + width)];
                    value = value == 255 ? 1 : 0;
                    inputNodes[count].setValue(value);
                    count++;
                }
                heightValue += heighStep * imageData.imageWidth;
            }
            while (count < numberOfInputs) {
                inputNodes[count].setValue(0);
                count++;
            }
        }
    }
}